﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Drawing;
using System.Windows.Forms.Layout;
using Moq;

namespace System.Windows.Forms.Tests;

[Collection("Sequential")] // workaround for WebBrowser control corrupting memory when run on multiple UI threads (instantiated via GUID)
public class ControlControlCollectionTests
{
    [WinFormsFact]
    public void ControlCollection_Ctor_Control()
    {
        using Control owner = new();
        Control.ControlCollection collection = new(owner);

        Assert.Empty(collection);
        Assert.False(collection.IsReadOnly);
        Assert.Same(owner, collection.Owner);
    }

    [WinFormsFact]
    public void ControlCollection_Ctor_NullOwner_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>("owner", () => new Control.ControlCollection(null));
    }

    [WinFormsFact]
    public void ControlCollection_Add_ControlNewCollection_Success()
    {
        using Control owner = new();
        using Control control1 = new();
        using Control control2 = new();
        var collection = new Control.ControlCollection(owner);
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(collection.Cast<Control>().Last(), e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        int layoutCallCount1 = 0;
        control1.Layout += (sender, e) => layoutCallCount1++;
        int layoutCallCount2 = 0;
        control2.Layout += (sender, e) => layoutCallCount2++;

        try
        {
            collection.Add(control1);
            Assert.Same(control1, Assert.Single(collection));
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Empty(owner.Controls);
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);

            // Add another.
            collection.Add(control2);
            Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Empty(owner.Controls);
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);

            // Add existing.
            Assert.Throws<ArgumentException>(() => collection.Add(control2));
            Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Empty(owner.Controls);
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);

            // Add null.
            collection.Add(null);
            Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_ControlExistingCollection_Success()
    {
        using Control owner = new();
        using Control control1 = new();
        using Control control2 = new();
        Control.ControlCollection collection = owner.Controls;
        int parentLayoutCallCount = 0;
        string affectedProperty = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(collection.Cast<Control>().Last(), e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        int layoutCallCount1 = 0;
        control1.Layout += (sender, e) => layoutCallCount1++;
        int layoutCallCount2 = 0;
        control2.Layout += (sender, e) => layoutCallCount2++;

        try
        {
            affectedProperty = "Parent";
            collection.Add(control1);
            Assert.Same(control1, Assert.Single(collection));
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(control1, Assert.Single(owner.Controls));
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);

            // Add another.
            collection.Add(control2);
            Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Equal(new Control[] { control1, control2 }, owner.Controls.Cast<Control>());
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);

            // Add existing.
            affectedProperty = "ChildIndex";
            collection.Add(control1);
            Assert.Equal(new Control[] { control2, control1 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Equal(new Control[] { control2, control1 }, owner.Controls.Cast<Control>());
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(3, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);

            // Add null.
            collection.Add(null);
            Assert.Equal(new Control[] { control2, control1 }, collection.Cast<Control>());
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_ControlHasParent_Success()
    {
        using Control owner1 = new();
        using Control owner2 = new();
        using Control control = new();
        Control.ControlCollection collection1 = owner1.Controls;
        Control.ControlCollection collection2 = owner2.Controls;
        int layoutCallCount = 0;
        string affectedProperty = "Parent";
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner2, sender);
            Assert.Same(collection2.Cast<Control>().Last(), e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            layoutCallCount++;
        }

        owner2.Layout += parentHandler;

        try
        {
            collection1.Add(control);
            collection2.Add(control);
            Assert.Same(control, Assert.Single(collection2));
            Assert.Empty(collection1);
            Assert.Same(owner2, control.Parent);
            Assert.Equal(0, control.TabIndex);
            Assert.Same(control, Assert.Single(owner2.Controls));
            Assert.Equal(1, layoutCallCount);
            Assert.False(owner2.IsHandleCreated);
            Assert.False(control.IsHandleCreated);
        }
        finally
        {
            owner2.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_ControlWithCustomLayoutEngine_Success()
    {
        using Control owner = new();
        Control.ControlCollection collection = owner.Controls;
        int layoutCallCount = 0;
        string affectedProperty = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(collection.Cast<Control>().Last(), e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            layoutCallCount++;
        }

        owner.Layout += parentHandler;

        using CustomLayoutEngineControl control = new();
        Mock<LayoutEngine> mockLayoutEngine = new(MockBehavior.Strict);
        mockLayoutEngine
            .Setup(e => e.InitLayout(control, BoundsSpecified.All))
            .Verifiable();
        control.SetLayoutEngine(mockLayoutEngine.Object);

        try
        {
            affectedProperty = "Parent";
            collection.Add(control);
            Assert.Same(control, Assert.Single(collection));
            Assert.Same(owner, control.Parent);
            Assert.Equal(0, control.TabIndex);
            Assert.Same(control, Assert.Single(owner.Controls));
            Assert.Equal(1, layoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control.IsHandleCreated);
            mockLayoutEngine.Verify(e => e.InitLayout(control, BoundsSpecified.All), Times.Once());

            // Add existing.
            affectedProperty = "ChildIndex";
            collection.Add(control);
            Assert.Same(control, Assert.Single(collection));
            Assert.Same(owner, control.Parent);
            Assert.Equal(0, control.TabIndex);
            Assert.Same(control, Assert.Single(owner.Controls));
            Assert.Equal(2, layoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control.IsHandleCreated);
            mockLayoutEngine.Verify(e => e.InitLayout(control, BoundsSpecified.All), Times.Once());
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_TabIndex_Success()
    {
        using SubControl owner = new();
        Control.ControlCollection collection = owner.Controls;
        using SubControl control1 = new()
        {
            TabIndex = 1
        };
        int tabIndexChangedCallCount1 = 0;
        control1.TabIndexChanged += (sender, e) => tabIndexChangedCallCount1++;

        collection.Add(control1);
        Assert.Equal(new Control[] { control1 }, collection.Cast<Control>());
        Assert.Same(owner, control1.Parent);
        Assert.Equal(1, control1.TabIndex);
        Assert.Equal(0, tabIndexChangedCallCount1);

        // Add another.
        using SubControl control2 = new();
        int tabIndexChangedCallCount2 = 0;
        control2.TabIndexChanged += (sender, e) => tabIndexChangedCallCount2++;
        collection.Add(control2);
        Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
        Assert.Same(owner, control1.Parent);
        Assert.Equal(1, control1.TabIndex);
        Assert.Equal(0, tabIndexChangedCallCount1);
        Assert.Same(owner, control2.Parent);
        Assert.Equal(2, control2.TabIndex);
        Assert.Equal(0, tabIndexChangedCallCount2);

        // Add another.
        control1.TabIndex = 10;
        using SubControl control3 = new();
        int tabIndexChangedCallCount3 = 0;
        control3.TabIndexChanged += (sender, e) => tabIndexChangedCallCount3++;
        collection.Add(control3);
        Assert.Equal(new Control[] { control1, control2, control3 }, collection.Cast<Control>());
        Assert.Same(owner, control1.Parent);
        Assert.Equal(10, control1.TabIndex);
        Assert.Equal(1, tabIndexChangedCallCount1);
        Assert.Same(owner, control2.Parent);
        Assert.Equal(2, control2.TabIndex);
        Assert.Equal(0, tabIndexChangedCallCount2);
        Assert.Same(owner, control3.Parent);
        Assert.Equal(11, control3.TabIndex);
        Assert.Equal(0, tabIndexChangedCallCount3);
    }

    [WinFormsTheory]
    [BoolData]
    public void ControlCollection_Add_InvokeValueWithoutHandleOwnerWithHandle_CreatedValueIfVisible(bool visible)
    {
        using Control owner = new();
        using Control control = new()
        {
            Visible = visible
        };
        Control.ControlCollection collection = owner.Controls;
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;

        collection.Add(control);
        Assert.Same(control, Assert.Single(collection));
        Assert.Same(owner, control.Parent);
        Assert.Equal(visible, control.Visible);
        Assert.Equal(visible, control.IsHandleCreated);
        Assert.True(owner.IsHandleCreated);
        Assert.Equal(0, parentInvalidatedCallCount);
        Assert.Equal(0, parentStyleChangedCallCount);
        Assert.Equal(0, parentCreatedCallCount);
    }

    [WinFormsTheory]
    [BoolData]
    public void ControlCollection_Add_InvokeValueWithHandleOwnerWithoutHandle_Success(bool visible)
    {
        using Control owner = new();
        using Control control = new()
        {
            Visible = visible
        };
        Control.ControlCollection collection = owner.Controls;
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        collection.Add(control);
        Assert.Same(control, Assert.Single(collection));
        Assert.Same(owner, control.Parent);
        Assert.Equal(visible, control.Visible);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.False(owner.IsHandleCreated);
    }

    [WinFormsTheory]
    [BoolData]
    public void ControlCollection_Add_InvokeValueWithHandleOwnerWithHandleControlNewCollection_Success(bool visible)
    {
        using Control owner = new();
        using Control control = new()
        {
            Visible = visible
        };
        var collection = new Control.ControlCollection(owner);
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        Assert.Throws<ArgumentException>(() => collection.Add(control));
        Assert.Same(control, Assert.Single(collection));
        Assert.Same(owner, control.Parent);
        Assert.Equal(visible, control.Visible);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.True(owner.IsHandleCreated);
        Assert.Equal(0, parentInvalidatedCallCount);
        Assert.Equal(0, parentStyleChangedCallCount);
        Assert.Equal(0, parentCreatedCallCount);
    }

    [WinFormsTheory]
    [BoolData]
    public void ControlCollection_Add_InvokeValueWithHandleOwnerWithHandleControlExistingCollection_Success(bool visible)
    {
        using Control owner = new();
        using Control control = new()
        {
            Visible = visible
        };
        Control.ControlCollection collection = owner.Controls;
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        collection.Add(control);
        Assert.Equal(owner.Handle, PInvoke.GetParent(control));
        Assert.Same(control, Assert.Single(collection));
        Assert.Same(owner, control.Parent);
        Assert.Equal(visible, control.Visible);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.True(owner.IsHandleCreated);
        Assert.Equal(0, parentInvalidatedCallCount);
        Assert.Equal(0, parentStyleChangedCallCount);
        Assert.Equal(0, parentCreatedCallCount);
    }

    [WinFormsFact]
    public void ControlCollection_Add_InvokeWithHandler_CallsControlAdded()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        Control.ControlCollection collection = owner.Controls;
        int parentChangedCallCount = 0;
        int callCount = 0;
        int parentLayoutCallCount = 0;
        child1.ParentChanged += (sender, e) =>
        {
            Assert.Same(child1, sender);
            Assert.Same(EventArgs.Empty, e);
            parentChangedCallCount++;
        };
        child2.ParentChanged += (sender, e) =>
        {
            Assert.Same(child2, sender);
            Assert.Same(EventArgs.Empty, e);
            parentChangedCallCount++;
        };
        object affectedControl = null;
        string affectedProperty = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(affectedControl, e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        void handler(object sender, ControlEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.Control);
            Assert.Equal(parentChangedCallCount - 1, callCount);
            Assert.Equal(parentLayoutCallCount - 1, callCount);
            callCount++;
        }

        try
        {
            // Call with handler.
            owner.ControlAdded += handler;
            affectedControl = child1;
            affectedProperty = "Parent";
            collection.Add(child1);
            Assert.Same(owner, child1.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.Equal(1, callCount);

            // Call again.
            affectedProperty = "ChildIndex";
            collection.Add(child1);
            Assert.Same(owner, child1.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.Equal(1, callCount);

            // Remove handler.
            owner.ControlAdded -= handler;
            affectedControl = child2;
            affectedProperty = "Parent";
            collection.Add(child2);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(2, parentChangedCallCount);
            Assert.Equal(3, parentLayoutCallCount);
            Assert.Equal(1, callCount);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_InvokeWithNonOverridenProperties_DoesNotCallPropertyHandlers()
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) => parentChangedCallCount++;
        control.EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        control.FontChanged += (sender, e) => fontChangedCallCount++;
        control.ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        control.BackColorChanged += (sender, e) => backColorChangedCallCount++;
        control.RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Add(control);
        Assert.Same(owner, control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(0, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(0, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(0, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(0, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(0, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.Equal(0, rightToLeftChangedCallCount);
        Assert.Null(control.BindingContext);
        Assert.Equal(0, bindingContextChangedCallCount);
    }

    [WinFormsFact]
    public void ControlCollection_Add_InvokeWithNonOverridenPropertiesWithHandle_DoesNotCallPropertyHandlers()
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner);
        Assert.NotEqual(IntPtr.Zero, control.Handle);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) => parentChangedCallCount++;
        control.EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        control.FontChanged += (sender, e) => fontChangedCallCount++;
        control.ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        control.BackColorChanged += (sender, e) => backColorChangedCallCount++;
        control.RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Add(control);
        Assert.Same(owner, control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(0, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(0, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(0, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(0, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(0, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.Equal(0, rightToLeftChangedCallCount);
        Assert.Null(control.BindingContext);
        Assert.Equal(1, bindingContextChangedCallCount);
    }

    public static IEnumerable<object[]> Add_OverridenProperties_TestData()
    {
        BindingContext parentContext = [];
        BindingContext childContext = [];
        yield return new object[] { parentContext, childContext, childContext };
        yield return new object[] { parentContext, null, parentContext };
        yield return new object[] { null, childContext, childContext };
        yield return new object[] { null, null, null };
    }

    [WinFormsTheory]
    [MemberData(nameof(Add_OverridenProperties_TestData))]
    public void ControlCollection_Add_InvokeWithOverridenProperties_CallsPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext)
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner);

        owner.Enabled = false;
        Assert.True(control.Enabled);
        owner.Visible = false;
        Assert.True(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Equal(Control.DefaultFont, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(bindingContext, control.BindingContext);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            Assert.Equal(enabledChangedCallCount, parentChangedCallCount);
            Assert.Equal(visibleChangedCallCount, parentChangedCallCount);
            Assert.Equal(fontChangedCallCount, parentChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(backColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, parentChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        control.EnabledChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, enabledChangedCallCount);
            Assert.Equal(visibleChangedCallCount, enabledChangedCallCount);
            Assert.Equal(fontChangedCallCount, enabledChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(backColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, enabledChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, enabledChangedCallCount);
            enabledChangedCallCount++;
        };
        control.VisibleChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(fontChangedCallCount, visibleChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(backColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, visibleChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, visibleChangedCallCount);
            visibleChangedCallCount++;
        };
        control.FontChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(backColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, fontChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, fontChangedCallCount);
            fontChangedCallCount++;
        };
        control.ForeColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(backColorChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, foreColorChangedCallCount);
            foreColorChangedCallCount++;
        };
        control.BackColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, backColorChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, backColorChangedCallCount);
            backColorChangedCallCount++;
        };
        control.RightToLeftChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(backColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, rightToLeftChangedCallCount);
            rightToLeftChangedCallCount++;
        };
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        try
        {
            collection.Add(control);
            Assert.Same(owner, control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.False(control.Enabled);
            Assert.Equal(1, enabledChangedCallCount);
            Assert.False(control.Visible);
            Assert.Equal(1, visibleChangedCallCount);
            Assert.Same(font, control.Font);
            Assert.Equal(1, fontChangedCallCount);
            Assert.Equal(Color.Red, control.ForeColor);
            Assert.Equal(1, foreColorChangedCallCount);
            Assert.Equal(Color.Blue, control.BackColor);
            Assert.Equal(1, backColorChangedCallCount);
            Assert.Equal(RightToLeft.Yes, control.RightToLeft);
            Assert.Equal(1, rightToLeftChangedCallCount);
            Assert.Same(expectedBindingContext, control.BindingContext);
            Assert.Equal(0, bindingContextChangedCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    public static IEnumerable<object[]> Add_OverridenPropertiesWithHandle_TestData()
    {
        BindingContext parentContext = [];
        BindingContext childContext = [];
        yield return new object[] { parentContext, childContext, childContext, 0 };
        yield return new object[] { parentContext, null, parentContext, 1 };
        yield return new object[] { null, childContext, childContext, 0 };
        yield return new object[] { null, null, null, 1 };
    }

    [WinFormsTheory]
    [MemberData(nameof(Add_OverridenPropertiesWithHandle_TestData))]
    public void ControlCollection_Add_InvokeWithOverridenPropertiesWithHandle_CallsPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext, int expectedBindingContextChangedCallCount)
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner);

        owner.Enabled = false;
        Assert.True(control.Enabled);
        owner.Visible = false;
        Assert.True(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Equal(Control.DefaultFont, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(bindingContext, control.BindingContext);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            Assert.Equal(enabledChangedCallCount, parentChangedCallCount);
            Assert.Equal(visibleChangedCallCount, parentChangedCallCount);
            Assert.Equal(fontChangedCallCount, parentChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(backColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        control.EnabledChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, enabledChangedCallCount);
            Assert.Equal(visibleChangedCallCount, enabledChangedCallCount);
            Assert.Equal(fontChangedCallCount, enabledChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(backColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, enabledChangedCallCount);
            enabledChangedCallCount++;
        };
        control.VisibleChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(fontChangedCallCount, visibleChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(backColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, visibleChangedCallCount);
            visibleChangedCallCount++;
        };
        control.FontChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(backColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, fontChangedCallCount);
            fontChangedCallCount++;
        };
        control.ForeColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(backColorChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, foreColorChangedCallCount);
            foreColorChangedCallCount++;
        };
        control.BackColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, backColorChangedCallCount);
            backColorChangedCallCount++;
        };
        control.RightToLeftChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(backColorChangedCallCount - 1, rightToLeftChangedCallCount);
            rightToLeftChangedCallCount++;
        };
        control.BindingContextChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, bindingContextChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, bindingContextChangedCallCount);
            Assert.Equal(visibleChangedCallCount - 1, bindingContextChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, bindingContextChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, bindingContextChangedCallCount);
            Assert.Equal(backColorChangedCallCount - 1, bindingContextChangedCallCount);
            bindingContextChangedCallCount++;
        };

        try
        {
            collection.Add(control);
            Assert.Same(owner, control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.False(control.Enabled);
            Assert.Equal(1, enabledChangedCallCount);
            Assert.False(control.Visible);
            Assert.Equal(1, visibleChangedCallCount);
            Assert.Same(font, control.Font);
            Assert.Equal(1, fontChangedCallCount);
            Assert.Equal(Color.Red, control.ForeColor);
            Assert.Equal(1, foreColorChangedCallCount);
            Assert.Equal(Color.Blue, control.BackColor);
            Assert.Equal(1, backColorChangedCallCount);
            Assert.Equal(RightToLeft.Yes, control.RightToLeft);
            Assert.Equal(1, rightToLeftChangedCallCount);
            Assert.Same(expectedBindingContext, control.BindingContext);
            Assert.Equal(expectedBindingContextChangedCallCount, bindingContextChangedCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(4, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(1, createdCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(Add_OverridenProperties_TestData))]
    public void ControlCollection_Add_InvokeWithOverridenPropertiesAxHost_DoesNotCallPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext)
    {
        using Control owner = new();
        using SubAxHost control = new("8856f961-340a-11d0-a96b-00c04fd705a2");
        var collection = new Control.ControlCollection(owner);

        owner.Enabled = false;
        Assert.True(control.Enabled);
        owner.Visible = false;
        Assert.True(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Equal(Control.DefaultFont, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.No, ((Control)control).RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(bindingContext, control.BindingContext);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        ((Control)control).EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        ((Control)control).FontChanged += (sender, e) => fontChangedCallCount++;
        ((Control)control).ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        ((Control)control).BackColorChanged += (sender, e) => backColorChangedCallCount++;
        ((Control)control).RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        ((Control)control).BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Add(control);
        try
        {
            Assert.Same(owner, control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.False(control.Enabled);
            Assert.Equal(0, enabledChangedCallCount);
            Assert.False(control.Visible);
            Assert.Equal(0, visibleChangedCallCount);
            Assert.Same(font, control.Font);
            Assert.Equal(0, fontChangedCallCount);
            Assert.Equal(Color.Red, control.ForeColor);
            Assert.Equal(0, foreColorChangedCallCount);
            Assert.Equal(Color.Blue, control.BackColor);
            Assert.Equal(0, backColorChangedCallCount);
            Assert.Equal(RightToLeft.Yes, ((Control)control).RightToLeft);
            Assert.Equal(0, rightToLeftChangedCallCount);
            Assert.Same(expectedBindingContext, control.BindingContext);
            Assert.Equal(0, bindingContextChangedCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(Add_OverridenPropertiesWithHandle_TestData))]
    public void ControlCollection_Add_InvokeWithOverridenPropertiesAxHostWithHandle_CallSPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext, int expectedBindingContextChangedCallCount)
    {
        using Control owner = new();
        using SubAxHost control = new("8856f961-340a-11d0-a96b-00c04fd705a2");
        var collection = new Control.ControlCollection(owner);

        owner.Enabled = false;
        Assert.True(control.Enabled);
        owner.Visible = false;
        Assert.True(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Equal(Control.DefaultFont, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.No, ((Control)control).RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(bindingContext, control.BindingContext);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        ((Control)control).EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        ((Control)control).FontChanged += (sender, e) => fontChangedCallCount++;
        ((Control)control).ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        ((Control)control).BackColorChanged += (sender, e) => backColorChangedCallCount++;
        ((Control)control).RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        ((Control)control).BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        try
        {
            collection.Add(control);
            Assert.Same(owner, control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.False(control.Enabled);
            Assert.Equal(1, enabledChangedCallCount);
            Assert.False(control.Visible);
            Assert.Equal(1, visibleChangedCallCount);
            Assert.Same(font, control.Font);
            Assert.Equal(1, fontChangedCallCount);
            Assert.Equal(Color.Red, control.ForeColor);
            Assert.Equal(1, foreColorChangedCallCount);
            Assert.Equal(Color.Blue, control.BackColor);
            Assert.Equal(1, backColorChangedCallCount);
            Assert.Equal(RightToLeft.Yes, ((Control)control).RightToLeft);
            Assert.Equal(1, rightToLeftChangedCallCount);
            Assert.Same(expectedBindingContext, control.BindingContext);
            Assert.Equal(expectedBindingContextChangedCallCount, bindingContextChangedCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(6, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    private class SubAxHost : AxHost
    {
        public SubAxHost(string clsid) : base(clsid)
        {
        }

        public SubAxHost(string clsid, int flags) : base(clsid, flags)
        {
        }
    }

    [WinFormsFact]
    public void ControlCollection_Add_TopLevelValue_ThrowsArgumentException()
    {
        using Control owner = new();
        using SubControl control = new();
        control.SetTopLevel(true);
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(control));
    }

    [Fact] // cross-thread access
    public void ControlCollection_Add_DifferentThreadValueOwner_ThrowsArgumentException()
    {
        Control owner = null;
        Thread thread = new(() =>
        {
            owner = new Control();
            Assert.NotEqual(IntPtr.Zero, owner.Handle);
        });
        thread.Start();
        thread.Join();

        using Control control = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(control));
    }

    [Fact] // cross-thread access
    public void ControlCollection_Add_DifferentThreadValueControl_ThrowsArgumentException()
    {
        Control control = null;
        Thread thread = new(() =>
        {
            control = new Control();
            Assert.NotEqual(IntPtr.Zero, control.Handle);
        });
        thread.Start();
        thread.Join();

        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(control));
    }

    [WinFormsFact]
    public void ControlCollection_Add_SameAsOwner_ThrowsArgumentException()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(owner));
    }

    [WinFormsFact]
    public void ControlCollection_Add_OwnerParent_ThrowsArgumentException()
    {
        using Control parent = new();
        using Control owner = new()
        {
            Parent = parent
        };
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(parent));
    }

    [WinFormsFact]
    public void ControlCollection_Add_OwnerGrandParent_ThrowsArgumentException()
    {
        using Control grandparent = new();
        using Control parent = new()
        {
            Parent = grandparent
        };
        using Control owner = new()
        {
            Parent = parent
        };
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>(() => collection.Add(grandparent));
    }

    [WinFormsFact]
    public void ControlCollection_AddRange_Invoke_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        Control.ControlCollection collection = owner.Controls;
        int parentLayoutCallCount = 0;
        object affectedControl = null;
        string affectedProperty = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(affectedControl, e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        int controlAddedCallCount = 0;
        owner.ControlAdded += (sender, e) => controlAddedCallCount++;

        try
        {
            affectedControl = child3;
            affectedProperty = "Parent";
            collection.AddRange([child1, child2, null, child3]);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Same(owner, child3.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.Equal(3, controlAddedCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            affectedControl = child1;
            affectedProperty = "ChildIndex";
            collection.AddRange([child1, child2, null, child3]);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Same(owner, child3.Parent);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.Equal(3, controlAddedCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Add empty.
            collection.AddRange(Array.Empty<Control>());
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Same(owner, child3.Parent);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.Equal(3, controlAddedCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_AddRange_NullControls_ThrowsArgumentNullException()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentNullException>("controls", () => collection.AddRange(null));
    }

    [WinFormsFact]
    public void ControlCollection_Clear_Invoke_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };
        int parentLayoutCallCount = 0;
        owner.Layout += (sender, e) =>
        {
            Assert.Same(owner, sender);
            Assert.Same(child3, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        };
        int controlRemovedCallCount = 0;
        owner.ControlRemoved += (sender, e) => controlRemovedCallCount++;

        collection.Clear();
        Assert.Empty(collection);
        Assert.Null(child1.Parent);
        Assert.Null(child2.Parent);
        Assert.Null(child3.Parent);
        Assert.Equal(1, parentLayoutCallCount);
        Assert.Equal(3, controlRemovedCallCount);
        Assert.False(owner.IsHandleCreated);
        Assert.False(child1.IsHandleCreated);
        Assert.False(child2.IsHandleCreated);
        Assert.False(child3.IsHandleCreated);

        // Clear again.
        collection.Clear();
        Assert.Empty(collection);
        Assert.Null(child1.Parent);
        Assert.Null(child2.Parent);
        Assert.Null(child3.Parent);
        Assert.Equal(1, parentLayoutCallCount);
        Assert.Equal(3, controlRemovedCallCount);
        Assert.False(owner.IsHandleCreated);
        Assert.False(child1.IsHandleCreated);
        Assert.False(child2.IsHandleCreated);
        Assert.False(child3.IsHandleCreated);
    }

    [WinFormsFact]
    public void ControlCollection_Clone_Invoke_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var sourceCollection = new Control.ControlCollection(owner);
        ICloneable iCloneable = sourceCollection;
        sourceCollection.Add(child1);
        sourceCollection.Add(child2);
        sourceCollection.Add(child3);
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            var collection = Assert.IsType<Control.ControlCollection>(iCloneable.Clone());
            Assert.NotSame(sourceCollection, collection);
            Assert.Equal(new Control[] { child1, child2, child3 }, sourceCollection.Cast<Control>());
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.False(collection.IsReadOnly);
            Assert.Same(owner, collection.Owner);
            Assert.Same(owner, child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(0, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Contains_Invoke_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.True(collection.Contains(child1));
        Assert.True(collection.Contains(child2));
        Assert.False(collection.Contains(new Control()));
        Assert.False(collection.Contains(null));
    }

    [WinFormsTheory]
    [InlineData(null, false)]
    [InlineData("", false)]
    [InlineData("name1", true)]
    [InlineData("NAME1", true)]
    [InlineData("name2", true)]
    [InlineData("NoSuchName", false)]
    [InlineData("abcd", false)]
    [InlineData("abcde", false)]
    [InlineData("abcdef", false)]
    public void ControlCollection_ContainsKey_Invoke_ReturnsExpected(string key, bool expected)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        Assert.Equal(expected, collection.ContainsKey(key));

        // Call again.
        Assert.Equal(expected, collection.ContainsKey(key));
        Assert.Equal(-1, collection.IndexOfKey("NoSuchKey"));
    }

    [WinFormsTheory]
    [InlineData("name2")]
    [InlineData("NAME2")]
    public void ControlCollection_Find_InvokeKeyExists_ReturnsExpected(string key)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        using Control grandchild1 = new()
        {
            Name = "name1"
        };
        using Control grandchild2 = new()
        {
            Name = "name2"
        };
        using Control grandchild3 = new()
        {
            Name = "name2"
        };
        child3.Controls.Add(grandchild1);
        child3.Controls.Add(grandchild2);
        child3.Controls.Add(grandchild3);
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);
        collection.Add(child3);

        // Search all children.
        Assert.Equal([child2, child3, grandchild2, grandchild3], collection.Find(key, searchAllChildren: true));

        // Call again.
        Assert.Equal([child2, child3, grandchild2, grandchild3], collection.Find(key, searchAllChildren: true));

        // Don't search all children.
        Assert.Equal([child2, child3], collection.Find(key, searchAllChildren: false));

        // Call again.
        Assert.Equal([child2, child3], collection.Find(key, searchAllChildren: false));
    }

    [WinFormsTheory]
    [InlineData("NoSuchName")]
    [InlineData("abcd")]
    [InlineData("abcde")]
    [InlineData("abcdef")]
    public void ControlCollection_Find_InvokeNoSuchKey_ReturnsEmpty(string key)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);
        collection.Add(child3);

        Assert.Empty(collection.Find(key, searchAllChildren: true));
        Assert.Empty(collection.Find(key, searchAllChildren: false));
    }

    [WinFormsTheory]
    [NullAndEmptyStringData]
    public void ControlCollection_Find_NullOrEmptyKey_ThrowsArgumentNullException(string key)
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentNullException>("key", () => collection.Find(key, searchAllChildren: true));
        Assert.Throws<ArgumentNullException>("key", () => collection.Find(key, searchAllChildren: false));
    }

    [WinFormsFact]
    public void ControlCollection_GetChildIndex_InvokeControl_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.Equal(0, collection.GetChildIndex(child1));
        Assert.Equal(1, collection.GetChildIndex(child2));
    }

    [WinFormsFact]
    public void ControlCollection_GetChildIndex_InvokeControlBoolThrowException_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.Equal(0, collection.GetChildIndex(child1, throwException: true));
        Assert.Equal(1, collection.GetChildIndex(child2, throwException: true));
    }

    [WinFormsFact]
    public void ControlCollection_GetChildIndex_InvokeControlBoolNoSuchControl_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.Equal(0, collection.GetChildIndex(child1, throwException: false));
        Assert.Equal(1, collection.GetChildIndex(child2, throwException: false));
        Assert.Equal(-1, collection.GetChildIndex(new Control(), throwException: false));
        Assert.Equal(-1, collection.GetChildIndex(null, throwException: false));
    }

    public static IEnumerable<object[]> GetChildIndex_NoSuchControl_TestData()
    {
        yield return new object[] { new Control() };
        yield return new object[] { null };
    }

    [WinFormsTheory]
    [MemberData(nameof(GetChildIndex_NoSuchControl_TestData))]
    public void ControlCollection_GetChildIndex_NoSuchControl_ThrowsArgumentException(Control child)
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.Throws<ArgumentException>(() => collection.GetChildIndex(child));
        Assert.Throws<ArgumentException>(() => collection.GetChildIndex(child));
    }

    [WinFormsFact]
    public void ControlCollection_GetEnumerator_InvokeEmpty_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        IEnumerator enumerator = collection.GetEnumerator();
        for (int i = 0; i < 2; i++)
        {
            Assert.Null(enumerator.Current);

            Assert.False(enumerator.MoveNext());
            Assert.Null(enumerator.Current);

            // Call again.
            Assert.False(enumerator.MoveNext());
            Assert.Null(enumerator.Current);

            enumerator.Reset();
        }
    }

    [WinFormsFact]
    public void ControlCollection_GetEnumerator_InvokeNotEmpty_ReturnsExpected()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        IEnumerator enumerator = collection.GetEnumerator();
        for (int i = 0; i < 2; i++)
        {
            Assert.Null(enumerator.Current);

            Assert.True(enumerator.MoveNext());
            Assert.Same(child1, enumerator.Current);

            Assert.True(enumerator.MoveNext());
            Assert.Same(child2, enumerator.Current);

            Assert.True(enumerator.MoveNext());
            Assert.Same(child3, enumerator.Current);

            Assert.False(enumerator.MoveNext());
            Assert.Same(child3, enumerator.Current);

            // Call again.
            Assert.False(enumerator.MoveNext());
            Assert.Same(child3, enumerator.Current);

            enumerator.Reset();
        }
    }

    [WinFormsFact]
    public void ControlCollection_GetEnumerator_AddDuringEnumeration_ReturnsExpected()
    {
        using Control owner = new();
        using Control child = new();
        var collection = new Control.ControlCollection(owner);
        IEnumerator enumerator = collection.GetEnumerator();
        collection.Add(child);
        for (int i = 0; i < 2; i++)
        {
            Assert.Null(enumerator.Current);

            Assert.False(enumerator.MoveNext());
            Assert.Null(enumerator.Current);

            // Call again.
            Assert.False(enumerator.MoveNext());
            Assert.Null(enumerator.Current);

            enumerator.Reset();
        }
    }

    [WinFormsFact]
    public void ControlCollection_GetEnumerator_InvokeRemoveBeforeEnumeration_ReturnsExpected()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        IEnumerator enumerator = collection.GetEnumerator();
        collection.Remove(child1);

        Assert.Null(enumerator.Current);

        Assert.True(enumerator.MoveNext());
        Assert.Same(child2, enumerator.Current);

        Assert.True(enumerator.MoveNext());
        Assert.Same(child3, enumerator.Current);

        Assert.False(enumerator.MoveNext());
        Assert.Same(child3, enumerator.Current);

        // Call again.
        Assert.False(enumerator.MoveNext());
        Assert.Same(child3, enumerator.Current);
    }

    [WinFormsFact]
    public void ControlCollection_GetEnumerator_InvokeRemoveAtEndOfEnumeration_ReturnsExpected()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        IEnumerator enumerator = collection.GetEnumerator();
        Assert.Null(enumerator.Current);

        Assert.True(enumerator.MoveNext());
        Assert.Same(child1, enumerator.Current);

        Assert.True(enumerator.MoveNext());
        Assert.Same(child2, enumerator.Current);

        Assert.True(enumerator.MoveNext());
        Assert.Same(child3, enumerator.Current);

        collection.Remove(child1);
        Assert.Throws<ArgumentOutOfRangeException>("index", () => enumerator.Current);

        Assert.False(enumerator.MoveNext());
        Assert.Throws<ArgumentOutOfRangeException>("index", () => enumerator.Current);

        // Call again.
        Assert.False(enumerator.MoveNext());
        Assert.Throws<ArgumentOutOfRangeException>("index", () => enumerator.Current);

        collection.Add(child1);
        Assert.Same(child1, enumerator.Current);
    }

    [WinFormsFact]
    public void ControlCollection_IndexOf_Invoke_ReturnsExpected()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        using Control child1 = new();
        using Control child2 = new();
        collection.Add(child1);
        collection.Add(child2);

        Assert.Equal(0, collection.IndexOf(child1));
        Assert.Equal(1, collection.IndexOf(child2));
        Assert.Equal(-1, collection.IndexOf(new Control()));
        Assert.Equal(-1, collection.IndexOf(null));
    }

    [WinFormsTheory]
    [InlineData(null, -1)]
    [InlineData("", -1)]
    [InlineData("name1", 0)]
    [InlineData("NAME1", 0)]
    [InlineData("name2", 1)]
    [InlineData("NoSuchName", -1)]
    [InlineData("abcd", -1)]
    [InlineData("abcde", -1)]
    [InlineData("abcdef", -1)]
    public void ControlCollection_IndexOfKey_Invoke_ReturnsExpected(string key, int expected)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        Assert.Equal(expected, collection.IndexOfKey(key));

        // Call again.
        Assert.Equal(expected, collection.IndexOfKey(key));
        Assert.Equal(-1, collection.IndexOfKey("NoSuchKey"));
    }

    [WinFormsTheory]
    [InlineData("name1", 0)]
    [InlineData("NAME1", 0)]
    [InlineData("name2", 1)]
    public void ControlCollection_Item_GetStringValidKey_ReturnsExpected(string key, int expectedIndex)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        Assert.Equal(collection[expectedIndex], collection[key]);

        // Call again.
        Assert.Equal(collection[expectedIndex], collection[key]);
        Assert.Null(collection["NoSuchKey"]);
    }

    [WinFormsTheory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("NoSuchName")]
    [InlineData("abcd")]
    [InlineData("abcde")]
    [InlineData("abcdef")]
    public void ControlCollection_Item_GetStringNoSuchKey_ReturnsNull(string key)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        Assert.Null(collection[key]);

        // Call again.
        Assert.Null(collection[key]);
        Assert.Null(collection["NoSuchKey"]);
    }

    [WinFormsFact]
    public void ControlCollection_Item_GetInt_ReturnsExpected()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        Assert.Same(child1, collection[0]);
        Assert.Same(child2, collection[1]);
        Assert.Same(child3, collection[2]);
    }

    [WinFormsTheory]
    [InlineData(-1)]
    [InlineData(0)]
    [InlineData(1)]
    public void ControlCollection_Item_GetInvalidIndexEmpty_ThrowsArgumentOutOfRangeException(int index)
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentOutOfRangeException>("index", () => collection[index]);
    }

    [WinFormsTheory]
    [InlineData(-1)]
    [InlineData(1)]
    [InlineData(2)]
    public void ControlCollection_Item_GetInvalidIndexNotEmpty_ThrowsArgumentOutOfRangeException(int index)
    {
        using Control owner = new();
        using Control child = new();
        var collection = new Control.ControlCollection(owner)
        {
            child
        };
        Assert.Throws<ArgumentOutOfRangeException>("index", () => collection[index]);
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeChildWithoutHandleOwnerWithoutHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2
        };

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove again.
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove null.
            collection.Remove(null);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeContainerControlParent_Success()
    {
        using ContainerControl owner = new();
        using Control child1 = new();
        using Control child2 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2
        };
        owner.ActiveControl = child1;
        Assert.Same(child1, owner.ActiveControl);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Null(owner.ActiveControl);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove again.
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Null(owner.ActiveControl);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove null.
            collection.Remove(null);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Null(owner.ActiveControl);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeChildWithHandleOwnerWithoutHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, child1.Handle);
        int invalidatedCallCount = 0;
        child1.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        child1.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        child1.HandleCreated += (sender, e) => createdCallCount++;

        try
        {
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);

            // Remove again.
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);

            // Remove null.
            collection.Remove(null);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeChildWithHandleOwnerWithHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int invalidatedCallCount = 0;
        child1.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        child1.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        child1.HandleCreated += (sender, e) => createdCallCount++;
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;

        try
        {
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);

            // Remove again.
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);

            // Remove null.
            collection.Remove(null);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeChildFromAnotherParent_Nop()
    {
        using Control owner1 = new();
        using Control owner2 = new();
        using Control control1 = new();
        using Control control2 = new();
        using Control control3 = new();
        var collection1 = new Control.ControlCollection(owner1);
        var collection2 = new Control.ControlCollection(owner2);
        collection1.Add(control1);
        collection2.Add(control2);

        // Remove with parent.
        collection1.Remove(control2);
        Assert.Equal(new Control[] { control1 }, collection1.Cast<Control>());
        Assert.Equal(new Control[] { control2 }, collection2.Cast<Control>());
        Assert.Same(owner1, control1.Parent);
        Assert.Same(owner2, control2.Parent);

        // Remove without parent.
        collection1.Remove(control3);
        Assert.Equal(new Control[] { control1 }, collection1.Cast<Control>());
        Assert.Equal(new Control[] { control2 }, collection2.Cast<Control>());
        Assert.Same(owner1, control1.Parent);
        Assert.Same(owner2, control2.Parent);
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeWithNonOverridenProperties_DoesNotCallPropertyHandlers()
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner)
        {
            control
        };

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) => parentChangedCallCount++;
        control.EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        control.FontChanged += (sender, e) => fontChangedCallCount++;
        control.ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        control.BackColorChanged += (sender, e) => backColorChangedCallCount++;
        control.RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Remove(control);
        Assert.Null(control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(0, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(0, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(0, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(0, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(0, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.Equal(0, rightToLeftChangedCallCount);
        Assert.Null(control.BindingContext);
        Assert.Equal(0, bindingContextChangedCallCount);
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeWithNonOverridenPropertiesWithHandle_DoesNotCallPropertyHandlers()
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner)
        {
            control
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) => parentChangedCallCount++;
        control.EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        control.FontChanged += (sender, e) => fontChangedCallCount++;
        control.ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        control.BackColorChanged += (sender, e) => backColorChangedCallCount++;
        control.RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Remove(control);
        Assert.Null(control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(0, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(2, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(0, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(0, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(0, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.Equal(0, rightToLeftChangedCallCount);
        Assert.Null(control.BindingContext);
        Assert.Equal(1, bindingContextChangedCallCount);
    }

    public static IEnumerable<object[]> Remove_OverridenProperties_TestData()
    {
        BindingContext parentContext = [];
        BindingContext childContext = [];
        yield return new object[] { parentContext, childContext, childContext };
        yield return new object[] { parentContext, null, parentContext };
        yield return new object[] { null, childContext, childContext };
        yield return new object[] { null, null, null };
    }

    [WinFormsTheory]
    [MemberData(nameof(Remove_OverridenProperties_TestData))]
    public void ControlCollection_Remove_InvokeWithOverridenProperties_CallsPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext)
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner)
        {
            control
        };

        owner.Enabled = false;
        Assert.False(control.Enabled);
        owner.Visible = false;
        Assert.False(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Same(font, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Color.Red, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Color.Blue, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.Yes, control.RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(expectedBindingContext, control.BindingContext);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) =>
        {
            Assert.Equal(enabledChangedCallCount, parentChangedCallCount);
            Assert.Equal(visibleChangedCallCount, parentChangedCallCount);
            Assert.Equal(fontChangedCallCount, parentChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(backColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, parentChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        };
        control.EnabledChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, enabledChangedCallCount);
            Assert.Equal(visibleChangedCallCount, enabledChangedCallCount);
            Assert.Equal(fontChangedCallCount, enabledChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(backColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, enabledChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, enabledChangedCallCount);
            enabledChangedCallCount++;
        };
        control.VisibleChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, visibleChangedCallCount);
            Assert.Equal(fontChangedCallCount, visibleChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(backColorChangedCallCount, visibleChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, visibleChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, visibleChangedCallCount);
            visibleChangedCallCount++;
        };
        control.FontChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(visibleChangedCallCount, fontChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(backColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, fontChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, fontChangedCallCount);
            fontChangedCallCount++;
        };
        control.ForeColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(backColorChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, foreColorChangedCallCount);
            foreColorChangedCallCount++;
        };
        control.BackColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(visibleChangedCallCount, backColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, backColorChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, backColorChangedCallCount);
            backColorChangedCallCount++;
        };
        control.RightToLeftChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(visibleChangedCallCount, rightToLeftChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(backColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(bindingContextChangedCallCount, rightToLeftChangedCallCount);
            rightToLeftChangedCallCount++;
        };
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Remove(control);
        Assert.Null(control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(1, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(0, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(1, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(1, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(1, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.Equal(1, rightToLeftChangedCallCount);
        Assert.Same(bindingContext, control.BindingContext);
        Assert.Equal(0, bindingContextChangedCallCount);
    }

    public static IEnumerable<object[]> Remove_OverridenPropertiesWithHandle_TestData()
    {
        BindingContext parentContext = [];
        BindingContext childContext = [];
        yield return new object[] { parentContext, childContext, childContext, 0 };
        yield return new object[] { parentContext, null, parentContext, 1 };
        yield return new object[] { null, childContext, childContext, 0 };
        yield return new object[] { null, null, null, 1 };
    }

    [WinFormsTheory]
    [MemberData(nameof(Remove_OverridenPropertiesWithHandle_TestData))]
    public void ControlCollection_Remove_InvokeWithOverridenPropertiesWithHandle_CallsPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext, int expectedBindingContextChangedCallCount)
    {
        using Control owner = new();
        using Control control = new();
        var collection = new Control.ControlCollection(owner)
        {
            control
        };

        owner.Enabled = false;
        Assert.False(control.Enabled);
        owner.Visible = false;
        Assert.False(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Same(font, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Color.Red, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Color.Blue, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.Yes, control.RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(expectedBindingContext, control.BindingContext);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            Assert.Equal(enabledChangedCallCount, parentChangedCallCount);
            Assert.Equal(fontChangedCallCount, parentChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(backColorChangedCallCount, parentChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        control.EnabledChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, enabledChangedCallCount);
            Assert.Equal(fontChangedCallCount, enabledChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(backColorChangedCallCount, enabledChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, enabledChangedCallCount);
            enabledChangedCallCount++;
        };
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        control.FontChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, fontChangedCallCount);
            Assert.Equal(foreColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(backColorChangedCallCount, fontChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, fontChangedCallCount);
            fontChangedCallCount++;
        };
        control.ForeColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, foreColorChangedCallCount);
            Assert.Equal(backColorChangedCallCount, foreColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, foreColorChangedCallCount);
            foreColorChangedCallCount++;
        };
        control.BackColorChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, backColorChangedCallCount);
            Assert.Equal(rightToLeftChangedCallCount, backColorChangedCallCount);
            backColorChangedCallCount++;
        };
        control.RightToLeftChanged += (sender, e) =>
        {
            Assert.Equal(parentChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(enabledChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(fontChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(foreColorChangedCallCount - 1, rightToLeftChangedCallCount);
            Assert.Equal(backColorChangedCallCount - 1, rightToLeftChangedCallCount);
            rightToLeftChangedCallCount++;
        };
        control.BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        try
        {
            collection.Remove(control);
            Assert.Null(control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.True(control.Enabled);
            Assert.Equal(1, enabledChangedCallCount);
            Assert.True(control.Visible);
            Assert.Equal(2, visibleChangedCallCount);
            Assert.Equal(Control.DefaultFont, control.Font);
            Assert.Equal(1, fontChangedCallCount);
            Assert.Equal(Control.DefaultForeColor, control.ForeColor);
            Assert.Equal(1, foreColorChangedCallCount);
            Assert.Equal(Control.DefaultBackColor, control.BackColor);
            Assert.Equal(1, backColorChangedCallCount);
            Assert.Equal(RightToLeft.No, control.RightToLeft);
            Assert.Equal(1, rightToLeftChangedCallCount);
            Assert.Same(bindingContext, control.BindingContext);
            Assert.Equal(expectedBindingContextChangedCallCount * 2, bindingContextChangedCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(4, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(1, createdCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(Remove_OverridenProperties_TestData))]
    public void ControlCollection_Remove_InvokeWithOverridenPropertiesAxHost_DoesNotCallPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext)
    {
        using Control owner = new();
        using SubAxHost control = new("8856f961-340a-11d0-a96b-00c04fd705a2");
        var collection = new Control.ControlCollection(owner)
        {
            control
        };

        owner.Enabled = false;
        Assert.False(control.Enabled);
        owner.Visible = false;
        Assert.False(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Same(font, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Color.Red, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Color.Blue, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.Yes, ((Control)control).RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(expectedBindingContext, control.BindingContext);

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        control.ParentChanged += (sender, e) => parentChangedCallCount++;
        ((Control)control).EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        ((Control)control).FontChanged += (sender, e) => fontChangedCallCount++;
        ((Control)control).ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        ((Control)control).BackColorChanged += (sender, e) => backColorChangedCallCount++;
        ((Control)control).RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        ((Control)control).BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        collection.Remove(control);
        Assert.Null(control.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.True(control.Enabled);
        Assert.Equal(0, enabledChangedCallCount);
        Assert.True(control.Visible);
        Assert.Equal(0, visibleChangedCallCount);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(0, fontChangedCallCount);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.Equal(0, foreColorChangedCallCount);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Equal(0, backColorChangedCallCount);
        Assert.Equal(RightToLeft.No, ((Control)control).RightToLeft);
        Assert.Equal(0, rightToLeftChangedCallCount);
        Assert.Same(bindingContext, control.BindingContext);
        Assert.Equal(0, bindingContextChangedCallCount);
    }

    [WinFormsTheory]
    [MemberData(nameof(Remove_OverridenPropertiesWithHandle_TestData))]
    public void ControlCollection_Remove_InvokeWithOverridenPropertiesAxHostWithHandle_CallSPropertyHandlers(BindingContext parentBindingContext, BindingContext bindingContext, BindingContext expectedBindingContext, int expectedBindingContextChangedCallCount)
    {
        using Control owner = new();
        using SubAxHost control = new("8856f961-340a-11d0-a96b-00c04fd705a2");
        var collection = new Control.ControlCollection(owner)
        {
            control
        };

        owner.Enabled = false;
        Assert.False(control.Enabled);
        owner.Visible = false;
        Assert.False(control.Visible);
        Font font = new("Arial", 8.25f);
        owner.Font = font;
        Assert.Same(font, control.Font);
        owner.ForeColor = Color.Red;
        Assert.Equal(Color.Red, control.ForeColor);
        owner.BackColor = Color.Blue;
        Assert.Equal(Color.Blue, control.BackColor);
        owner.RightToLeft = RightToLeft.Yes;
        Assert.Equal(RightToLeft.Yes, ((Control)control).RightToLeft);
        owner.BindingContext = parentBindingContext;
        control.BindingContext = bindingContext;
        Assert.Same(expectedBindingContext, control.BindingContext);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        int parentChangedCallCount = 0;
        int enabledChangedCallCount = 0;
        int visibleChangedCallCount = 0;
        int fontChangedCallCount = 0;
        int foreColorChangedCallCount = 0;
        int backColorChangedCallCount = 0;
        int rightToLeftChangedCallCount = 0;
        int bindingContextChangedCallCount = 0;
        void parentChangedHandler(object sender, EventArgs e)
        {
            parentChangedCallCount++;
        }

        control.ParentChanged += parentChangedHandler;
        ((Control)control).EnabledChanged += (sender, e) => enabledChangedCallCount++;
        control.VisibleChanged += (sender, e) => visibleChangedCallCount++;
        ((Control)control).FontChanged += (sender, e) => fontChangedCallCount++;
        ((Control)control).ForeColorChanged += (sender, e) => foreColorChangedCallCount++;
        ((Control)control).BackColorChanged += (sender, e) => backColorChangedCallCount++;
        ((Control)control).RightToLeftChanged += (sender, e) => rightToLeftChangedCallCount++;
        ((Control)control).BindingContextChanged += (sender, e) => bindingContextChangedCallCount++;

        try
        {
            collection.Remove(control);
            Assert.Null(control.Parent);
            Assert.Equal(1, parentChangedCallCount);
            Assert.True(control.Enabled);
            Assert.Equal(1, enabledChangedCallCount);
            Assert.True(control.Visible);
            Assert.Equal(2, visibleChangedCallCount);
            Assert.Equal(Control.DefaultFont, control.Font);
            Assert.Equal(1, fontChangedCallCount);
            Assert.Equal(Control.DefaultForeColor, control.ForeColor);
            Assert.Equal(1, foreColorChangedCallCount);
            Assert.Equal(Control.DefaultBackColor, control.BackColor);
            Assert.Equal(1, backColorChangedCallCount);
            Assert.Equal(RightToLeft.No, ((Control)control).RightToLeft);
            Assert.Equal(1, rightToLeftChangedCallCount);
            Assert.Same(bindingContext, control.BindingContext);
            Assert.Equal(expectedBindingContextChangedCallCount, bindingContextChangedCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(7, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(1, createdCallCount);
        }
        finally
        {
            control.ParentChanged -= parentChangedHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_Remove_InvokeWithHandler_CallsControlRemoved()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2
        };

        int parentChangedCallCount = 0;
        int callCount = 0;
        int parentLayoutCallCount = 0;
        child1.ParentChanged += (sender, e) =>
        {
            Assert.Same(child1, sender);
            Assert.Same(EventArgs.Empty, e);
            Assert.Equal(callCount, parentChangedCallCount);
            Assert.Equal(parentLayoutCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        };
        child2.ParentChanged += (sender, e) =>
        {
            Assert.Same(child2, sender);
            Assert.Same(EventArgs.Empty, e);
            Assert.Equal(callCount, parentChangedCallCount);
            Assert.Equal(parentLayoutCallCount, parentChangedCallCount);
            parentChangedCallCount++;
        };
        object affectedControl = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(affectedControl, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            Assert.Equal(parentChangedCallCount - 1, parentLayoutCallCount);
            Assert.Equal(callCount, parentLayoutCallCount);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        void handler(object sender, ControlEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.Control);
            Assert.Equal(parentChangedCallCount - 1, callCount);
            Assert.Equal(parentLayoutCallCount - 1, callCount);
            callCount++;
        }

        // Call with handler.
        owner.ControlRemoved += handler;
        affectedControl = child1;
        collection.Remove(child1);
        Assert.Null(child1.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.Equal(1, parentLayoutCallCount);
        Assert.Equal(1, callCount);

        // Call again.
        collection.Remove(child1);
        Assert.Null(child1.Parent);
        Assert.Equal(1, parentChangedCallCount);
        Assert.Equal(1, parentLayoutCallCount);
        Assert.Equal(1, callCount);

        // Remove handler.
        owner.ControlRemoved -= handler;
        affectedControl = child2;
        collection.Remove(child2);
        Assert.Null(child2.Parent);
        Assert.Equal(2, parentChangedCallCount);
        Assert.Equal(2, parentLayoutCallCount);
        Assert.Equal(1, callCount);
    }

    [WinFormsFact]
    public void ControlCollection_RemoveAt_Invoke_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2
        };

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        object affectedControl = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(affectedControl, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            affectedControl = child2;
            collection.RemoveAt(1);
            Assert.Equal(new Control[] { child1 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Null(child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove again.
            affectedControl = child1;
            collection.RemoveAt(0);
            Assert.Empty(collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Null(child2.Parent);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [InlineData(-1)]
    [InlineData(0)]
    [InlineData(1)]
    public void ControlCollection_RemoveAt_InvalidIndexEmpty_ThrowsArgumentOutOfRangeException(int index)
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.RemoveAt(index));
    }

    [WinFormsTheory]
    [InlineData(-1)]
    [InlineData(1)]
    [InlineData(2)]
    public void ControlCollection_RemoveAtInvalidIndexNotEmpty_ThrowsArgumentOutOfRangeException(int index)
    {
        using Control owner = new();
        using Control child = new();
        var collection = new Control.ControlCollection(owner)
        {
            child
        };
        Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.RemoveAt(index));
    }

    [WinFormsTheory]
    [InlineData("name2")]
    [InlineData("NAME2")]
    public void ControlCollection_RemoveByKey_InvokeValidKey_ReturnsExpected(string key)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        int layoutCallCount = 0;
        child2.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        object affectedControl = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(affectedControl, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            affectedControl = child2;
            collection.RemoveByKey(key);
            Assert.Equal(new Control[] { child1, child3 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Null(child2.Parent);
            Assert.Same(owner, child3.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Remove again.
            affectedControl = child3;
            collection.RemoveByKey(key);
            Assert.Equal(new Control[] { child1 }, collection.Cast<Control>());
            Assert.Same(owner, child1.Parent);
            Assert.Null(child2.Parent);
            Assert.Null(child3.Parent);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("NoSuchName")]
    [InlineData("abcd")]
    [InlineData("abcde")]
    [InlineData("abcdef")]
    public void ControlCollection_RemoveByKey_InvokeNoSuchKey_ReturnsNull(string key)
    {
        using Control owner = new();
        using Control child1 = new()
        {
            Name = "name1"
        };
        using Control child2 = new()
        {
            Name = "name2"
        };
        using Control child3 = new()
        {
            Name = "name2"
        };
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        collection.RemoveByKey(key);
        Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());

        // Call again.
        collection.RemoveByKey(key);
        Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_InvokeChildWithoutHandleOwnerWithoutHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("ChildIndex", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            // Set middle.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Set same.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Set start.
            collection.SetChildIndex(child1, 0);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [InlineData(2, 1)]
    [InlineData(3, 2)]
    [InlineData(4, 2)]
    public void ControlCollection_SetChildIndex_InvokeLargeIndex_Success(int index, int expectedParentLayoutCallCount)
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        var collection = new Control.ControlCollection(owner)
        {
            child1,
            child2,
            child3
        };

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("ChildIndex", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            // Set middle.
            collection.SetChildIndex(child1, index);
            Assert.Equal(new Control[] { child2, child3, child1 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Set same.
            collection.SetChildIndex(child1, index);
            Assert.Equal(new Control[] { child2, child3, child1 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_InvokeChildWithHandleOwnerWithoutHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);
        collection.Add(child3);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("ChildIndex", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, child1.Handle);
        int invalidatedCallCount = 0;
        child1.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        child1.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        child1.HandleCreated += (sender, e) => createdCallCount++;

        try
        {
            // Set middle.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Set same.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);

            // Set start.
            collection.SetChildIndex(child1, 0);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.False(child2.IsHandleCreated);
            Assert.False(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_InvokeChildWithHandleOwnerWithHandle_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);
        collection.Add(child3);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("ChildIndex", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int invalidatedCallCount = 0;
        child1.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        child1.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        child1.HandleCreated += (sender, e) => createdCallCount++;
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;

        try
        {
            // Set middle.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);

            // Set same.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);

            // Set start.
            collection.SetChildIndex(child1, 0);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.True(child1.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_InvokeChildWithoutHandleOwnerWithHandle_Success()
    {
        using Control owner = new();
        using SubControl child1 = new();
        using Control child2 = new();
        using Control child3 = new();
        Control.ControlCollection collection = owner.Controls;
        collection.Add(child1);
        collection.Add(child2);
        collection.Add(child3);

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("ChildIndex", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, owner.Handle);
        int parentInvalidatedCallCount = 0;
        owner.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        owner.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        owner.HandleCreated += (sender, e) => parentCreatedCallCount++;

        try
        {
            // Set middle.
            child1.DestroyHandle();
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.False(child1.IsHandleCreated);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);

            // Set same.
            collection.SetChildIndex(child1, 1);
            Assert.Equal(new Control[] { child2, child1, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.False(child1.IsHandleCreated);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);

            // Set start.
            collection.SetChildIndex(child1, 0);
            Assert.Equal(new Control[] { child1, child2, child3 }, collection.Cast<Control>());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.True(owner.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
            Assert.False(child1.IsHandleCreated);
            Assert.True(child2.IsHandleCreated);
            Assert.True(child3.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_NullChild_ThrowsArgumentNullException()
    {
        using Control owner = new();
        var collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentNullException>("child", () => collection.SetChildIndex(null, 0));
    }

    [WinFormsFact]
    public void ControlCollection_SetChildIndex_InvalidIndex_ThrowsArgumentOutOfRangeException()
    {
        using Control owner = new();
        using Control child = new();
        var collection = new Control.ControlCollection(owner)
        {
            child
        };
        Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.SetChildIndex(child, -2));
    }

    [WinFormsFact]
    public void ControlCollection_IListAdd_Invoke_Success()
    {
        using Control owner = new();
        using Control control1 = new();
        using Control control2 = new();
        IList collection = owner.Controls;
        int parentLayoutCallCount = 0;
        string affectedProperty = null;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(collection.Cast<Control>().Last(), e.AffectedControl);
            Assert.Equal(affectedProperty, e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;
        int layoutCallCount1 = 0;
        control1.Layout += (sender, e) => layoutCallCount1++;
        int layoutCallCount2 = 0;
        control2.Layout += (sender, e) => layoutCallCount2++;

        try
        {
            affectedProperty = "Parent";
            Assert.Equal(0, collection.Add(control1));
            Assert.Same(control1, Assert.Single(collection));
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(control1, Assert.Single(owner.Controls));
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);

            // Add another.
            affectedProperty = "Parent";
            Assert.Equal(1, collection.Add(control2));
            Assert.Equal(new Control[] { control1, control2 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Equal(new Control[] { control1, control2 }, owner.Controls.Cast<Control>());
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(2, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);

            // Add existing.
            affectedProperty = "ChildIndex";
            Assert.Equal(1, collection.Add(control1));
            Assert.Equal(new Control[] { control2, control1 }, collection.Cast<Control>());
            Assert.Same(owner, control1.Parent);
            Assert.Equal(0, control1.TabIndex);
            Assert.Same(owner, control2.Parent);
            Assert.Equal(1, control2.TabIndex);
            Assert.Equal(new Control[] { control2, control1 }, owner.Controls.Cast<Control>());
            Assert.Equal(0, layoutCallCount1);
            Assert.Equal(0, layoutCallCount2);
            Assert.Equal(3, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(control1.IsHandleCreated);
            Assert.False(control2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_IListAdd_NotControl_ThrowsArgumentException()
    {
        using Control owner = new();
        IList collection = new Control.ControlCollection(owner);
        Assert.Throws<ArgumentException>("control", () => collection.Add(new object()));
        Assert.Throws<ArgumentException>("control", () => collection.Add(null));
    }

    [WinFormsFact]
    public void ControlCollection_IListRemove_Invoke_Success()
    {
        using Control owner = new();
        using Control child1 = new();
        using Control child2 = new();
        IList collection = new Control.ControlCollection(owner)
        {
            child1,
            child2
        };

        int layoutCallCount = 0;
        child1.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(owner, sender);
            Assert.Same(child1, e.AffectedControl);
            Assert.Equal("Parent", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        owner.Layout += parentHandler;

        try
        {
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove again.
            collection.Remove(child1);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);

            // Remove null.
            collection.Remove(null);
            Assert.Equal(new Control[] { child2 }, collection.Cast<Control>());
            Assert.Null(child1.Parent);
            Assert.Same(owner, child2.Parent);
            Assert.Equal(1, parentLayoutCallCount);
            Assert.False(owner.IsHandleCreated);
            Assert.False(child1.IsHandleCreated);
            Assert.False(child2.IsHandleCreated);
        }
        finally
        {
            owner.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void ControlCollection_IListRemove_NotControl_ThrowsArgumentException()
    {
        using Control owner = new();
        IList collection = new Control.ControlCollection(owner);
        collection.Remove(new object());
        collection.Remove(null);
    }

    private class CustomLayoutEngineControl : Control
    {
        private LayoutEngine _layoutEngine;

        public CustomLayoutEngineControl()
        {
            _layoutEngine = new Control().LayoutEngine;
        }

        public void SetLayoutEngine(LayoutEngine layoutEngine)
        {
            _layoutEngine = layoutEngine;
        }

        public override LayoutEngine LayoutEngine => _layoutEngine;

        public new void InitLayout() => base.InitLayout();
    }

    private class SubControl : Control
    {
        public new void DestroyHandle() => base.DestroyHandle();

        public new void SetTopLevel(bool value) => base.SetTopLevel(value);
    }
}
